package com.qianfeng.iot.auth.service.impl;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.dc3.common.bean.R;
import com.dc3.common.constant.CacheConstant;
import com.dc3.common.utils.Dc3Util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianfeng.iot.auth.service.TokenService;
import com.qianfeng.iot.openfeign.CacheClient;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Created by Jackiechan on 2022/7/19 15:20
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
public class TokenServiceImpl implements TokenService {

    private CacheClient cacheClient;

    @Autowired
    public void setCacheClient(CacheClient cacheClient) {
        this.cacheClient = cacheClient;
    }

    private ObjectMapper objectMapper;

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public Mono<Integer> checkToken(String token) {
        //获取到他们的盐
        //盐在redis中保存着,key是租户名加上用户名加上salt
        //我们需要先获取到key,key在哪?在令牌的原始数据中保存着
        //我们用原始数据中的数据查询到盐,然后用这个盐进行校验,如果它是乱写的,肯定校验失败,如果是对的,才会校验成功
        return Mono.just(token)
                .map(jwt -> jwt.split("\\."))//按照.进行分割
                .map(strings -> strings[1])//1下标的数据就是我们的原始数据
                .doOnError(throwable -> {
                    System.err.println(throwable.getMessage() + "越界了");
                })
                .map(body -> Dc3Util.decode(body))//body现在是一个base64的数据,需要先转回来,变成一个json
                .map(json -> {
                    try {
                        return objectMapper.readValue(json, Map.class); //解析原始内容
                    } catch (IOException e) {
                        throw new RuntimeException("令牌错误,请检查");
                    }
                })//将json转成map
                .flatMap(map -> {
                    //获取到原始内容中的用户的名字和租户的名字
                    String userName = (String) map.get("sub");
                    String tenantName = (String) map.get("tenantName");
                    //从redis中获取数据
                    String key = CacheConstant.Entity.TENANT + tenantName + CacheConstant.Entity.USER + userName + CacheConstant.Suffix.SALT;
                    return cacheClient.get(key).map(R::getData);//获取到盐
                    //TODO 如果redis中保存的时候jwt地方数据,在这里获取到就是jwt数据
                }).flatMap(slat -> {
                    //只要不对,就会出现异常,如果没有问题就不会有异常

                    try {
                        Jwt jwt = Jwts.parser().setSigningKey(slat.toString().getBytes(StandardCharsets.UTF_8)).parse(token);
                        return Mono.just(1);//如果没有问题就返回1
                    } catch (Exception e) {
                        //可能会过期,或者数据不符合jwt的解析要求,会抛出异常
                        e.printStackTrace();
                    }
                    //TODO 如果上面获取到的是jwt的内容,在这里应该进行比较,看看内容是否一致, 一致就代表尚未过期,可以放行,不一致就代表不对
                    return Mono.just(0);
                })
                // .onErrorReturn(0)
                .onErrorReturn(throwable -> {//当发生错误的时候执行
                    System.err.println(throwable.getMessage());
                    return true;//返回true就是用当前的返回值,返回false就是不处理这个异常,我们可以在这里根据业务决定返回什么值
                }, 0)
                .defaultIfEmpty(0);//如果不行就返回0;
    }
}
